/*
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author            Notes
 * 2024-08-19     Tian Yiqing       the first version
 * 
 */

#include <board.h>
#include <rtdevice.h>
#include <drivers/hwtimer.h>

#ifdef RT_USING_HWTIMER

typedef struct {
    uint32_t timer_periph;
    IRQn_Type irqn;
    rcu_periph_enum rcu;
} gd32_hwtimer_data;

typedef struct {
    char dev_name[RT_NAME_MAX];
    const gd32_hwtimer_data hw_data;
    rt_hwtimer_t hwtimer_dev;
    const struct rt_hwtimer_info hwtimer_info;
} gd32_hwtimer_device;

enum 
{
#ifdef BSP_USING_TIM0
    TIM0_INDEX,
#endif
#ifdef BSP_USING_TIM1
    TIM1_INDEX,
#endif
#ifdef BSP_USING_TIM2
    TIM2_INDEX,
#endif
#ifdef BSP_USING_TIM3
    TIM3_INDEX,
#endif
#ifdef BSP_USING_TIM4
    TIM4_INDEX,
#endif
#ifdef BSP_USING_TIM5
    TIM5_INDEX,
#endif
#ifdef BSP_USING_TIM6
    TIM6_INDEX,
#endif
#ifdef BSP_USING_TIM7
    TIM7_INDEX,
#endif
#ifdef BSP_USING_TIM8
    TIM8_INDEX,
#endif
#ifdef BSP_USING_TIM9
    TIM9_INDEX,
#endif
#ifdef BSP_USING_TIM10
    TIM10_INDEX,
#endif
#ifdef BSP_USING_TIM11
    TIM11_INDEX,
#endif
#ifdef BSP_USING_TIM12
    TIM12_INDEX,
#endif
#ifdef BSP_USING_TIM13
    TIM13_INDEX,
#endif
};

/*
 * static void __set_timerx_freq
 * Set freq with timerx
 *
 * @param timerx the pointer of TIMER_TypeDef
 * @param freq of the timer clock
 * @retval None
 */
static void __set_timerx_freq(uint32_t timerx, uint32_t freq)
{
    uint32_t apbxfreq, timerfreq, ahbfreq;
    uint16_t prescaler;
    uint32_t temp;

    uint32_t timersel = RCU_CFG1 & RCU_CFG1_TIMERSEL;
    timersel = timersel >> 24;

    ahbfreq = rcu_clock_freq_get(CK_AHB);

    if (timerx == TIMER0 || timerx == TIMER7 || timerx == TIMER8 \
        || timerx == TIMER9 || timerx == TIMER10)
    {
        apbxfreq = rcu_clock_freq_get(CK_APB2);
        temp = RCU_CFG0 & RCU_CFG0_APB2PSC;
        temp >>= 13;
    }
    else
    {
        apbxfreq = rcu_clock_freq_get(CK_APB1);
        temp = RCU_CFG0 & RCU_CFG0_APB1PSC;
        temp >>= 10;
    }

    if(timersel == 0)
    {
        if(temp <= 4) // timersel = 0 && apbx psc = 1 or 2
            timerfreq = ahbfreq;
        else
            timerfreq = apbxfreq << 1;
    }
    else
    {
        if(temp <= 5) // timersel = 1 && apbx psc = 1 or 2 or 4
            timerfreq = ahbfreq;
        else
            timerfreq = apbxfreq << 2;
    }

    prescaler = timerfreq / freq - 1;

    timer_prescaler_config(timerx, prescaler, TIMER_PSC_RELOAD_NOW);
}

static void gd32_hwtimer_init(struct rt_hwtimer_device *timer, rt_uint32_t state)
{
    gd32_hwtimer_device *dev = (gd32_hwtimer_device*)timer->parent.user_data;
    uint32_t timer_periph = dev->hw_data.timer_periph;
    timer_parameter_struct initpara;

    if (state)
    {
        timer_internal_clock_config(timer_periph);
        timer_struct_para_init(&initpara);
        initpara.alignedmode = TIMER_COUNTER_EDGE;
        initpara.clockdivision = TIMER_CKDIV_DIV1;
        initpara.counterdirection = TIMER_COUNTER_UP;
        initpara.period =  timer->info->maxcnt;
        timer_init(timer_periph, &initpara);
        __set_timerx_freq(timer_periph, timer->info->maxfreq);
    }
}

static rt_err_t gd32_hwtimer_start(struct rt_hwtimer_device *timer, \
    rt_uint32_t cnt, rt_hwtimer_mode_t mode)
{
    gd32_hwtimer_device *dev = (gd32_hwtimer_device*)timer->parent.user_data;
    uint32_t timer_periph = dev->hw_data.timer_periph;

    if (mode == HWTIMER_MODE_ONESHOT)
    {
        timer_single_pulse_mode_config(timer_periph, TIMER_SP_MODE_SINGLE);
    }
    else if (mode == HWTIMER_MODE_PERIOD)
    {
        timer_single_pulse_mode_config(timer_periph, TIMER_SP_MODE_REPETITIVE);
    }

    timer_counter_value_config(timer_periph, 0);
    timer_autoreload_value_config(timer_periph, cnt - 1);
    timer_enable(timer_periph);

    return 0;
}

static void gd32_hwtimer_stop(struct rt_hwtimer_device *timer)
{
    gd32_hwtimer_device *dev = (gd32_hwtimer_device*)timer->parent.user_data;
    uint32_t timer_periph = dev->hw_data.timer_periph;

    timer_disable(timer_periph);
}

static rt_uint32_t gd32_hwtimer_count_get(struct rt_hwtimer_device *timer)
{
    gd32_hwtimer_device *dev = (gd32_hwtimer_device*)timer->parent.user_data;
    uint32_t timer_periph = dev->hw_data.timer_periph;
    rt_uint32_t count;

    count = timer_counter_read(timer_periph);

    return count;
}

static rt_err_t gd32_hwtimer_control(struct rt_hwtimer_device *timer, rt_uint32_t cmd, \
    void *args)
{
    int ret = RT_EOK;
    rt_int32_t freq;

    gd32_hwtimer_device *dev = (gd32_hwtimer_device*)timer->parent.user_data;
    uint32_t timer_periph = dev->hw_data.timer_periph;

    switch (cmd)
    {
    case HWTIMER_CTRL_FREQ_SET:
        freq = *(rt_uint32_t *)args;
        __set_timerx_freq(timer_periph, freq);
        break;
    default:
        rt_kprintf("invalid cmd:%x\n", cmd);
        ret = -RT_EINVAL;
        break;
    }

    return ret;
}

static const struct rt_hwtimer_ops g_gd32_hwtimer_ops = {
    gd32_hwtimer_init,
    gd32_hwtimer_start,
    gd32_hwtimer_stop,
    gd32_hwtimer_count_get,
    gd32_hwtimer_control,
};

static gd32_hwtimer_device g_gd32_hwtimer[] = {
#ifdef BSP_USING_TIM0
    {
        "timer0",
        {
             TIMER0,
             TIMER0_UP_TIMER9_IRQn,
             RCU_TIMER0,
        },
        {0},
        {
            1000000,
            1000,
            0xffff,
            HWTIMER_CNTMODE_UP, /* count up mode  */
        }
    },
#endif
#ifdef BSP_USING_TIM1
    {
        "timer1",
        {
             TIMER1,
             TIMER1_IRQn,
             RCU_TIMER1,
        },
        {0},
        {
            1000000,
            1000,
            0xffff,
            HWTIMER_CNTMODE_UP, /* count up mode  */
        }
    },
#endif
#ifdef BSP_USING_TIM2
    {
        "timer2",
        {
             TIMER2,
             TIMER2_IRQn,
             RCU_TIMER2,
        },
        {0},
        {
            1000000,
            1000,
            0xffff,
            HWTIMER_CNTMODE_UP, /* count up mode  */
        }
    },
#endif
#ifdef BSP_USING_TIM3
    {
        "timer3",
        {
             TIMER3,
             TIMER3_IRQn,
             RCU_TIMER3,
        },
        {0},
        {
            1000000,
            1000,
            0xffff,
            HWTIMER_CNTMODE_UP, /* count up mode  */
        }
    },
#endif
#ifdef BSP_USING_TIM4
    {
        "timer4",
        {
             TIMER4,
             TIMER4_IRQn,
             RCU_TIMER4,
        },
        {0},
        {
            1000000,
            1000,
            0xffff,
            HWTIMER_CNTMODE_UP, /* count up mode  */
        }
    },
#endif
#ifdef BSP_USING_TIM5
    {
        "timer5",
        {
             TIMER5,
             TIMER5_DAC_IRQn,
             RCU_TIMER5,
        },
        {0},
        {
            1000000,
            1000,
            0xffff,
            HWTIMER_CNTMODE_UP, /* count up mode  */
        }
    },
#endif
#ifdef BSP_USING_TIM6
    {
        "timer6",
        {
             TIMER6,
             TIMER6_IRQn,
             RCU_TIMER6,
        },
        {0},
        {
            1000000,
            1000,
            0xffff,
            HWTIMER_CNTMODE_UP, /* count up mode  */
        }
    },
#endif
#ifdef BSP_USING_TIM7
    {
        "timer7",
        {
             TIMER7,
             TIMER7_UP_TIMER12_IRQn,
             RCU_TIMER7,
        },
        {0},
        {
            1000000,
            1000,
            0xffff,
            HWTIMER_CNTMODE_UP, /* count up mode  */
        }
    },
#endif
#ifdef BSP_USING_TIM8
    {
        "timer8",
        {
             TIMER8,
             TIMER0_BRK_TIMER8_IRQn,
             RCU_TIMER8,
        },
        {0},
        {
            1000000,
            1000,
            0xffff,
            HWTIMER_CNTMODE_UP, /* count up mode  */
        }
    },
#endif
#ifdef BSP_USING_TIM9
    {
        "timer9",
        {
             TIMER9,
             TIMER0_UP_TIMER9_IRQn,
             RCU_TIMER9,
        },
        {0},
        {
            1000000,
            1000,
            0xffff,
            HWTIMER_CNTMODE_UP, /* count up mode  */
        }
    },
#endif
#ifdef BSP_USING_TIM10
    {
        "timer10",
        {
             TIMER10,
             TIMER0_TRG_CMT_TIMER10_IRQn,
             RCU_TIMER10,
        },
        {0},
        {
            1000000,
            1000,
            0xffff,
            HWTIMER_CNTMODE_UP, /* count up mode  */
        }
    },
#endif
#ifdef BSP_USING_TIM11
    {
        "timer11",
        {
             TIMER11,
             TIMER7_BRK_TIMER11_IRQn,
             RCU_TIMER11,
        },
        {0},
        {
            1000000,
            1000,
            0xffff,
            HWTIMER_CNTMODE_UP, /* count up mode  */
        }
    },
#endif
#ifdef BSP_USING_TIM12
    {
        "timer12",
        {
             TIMER12,
             TIMER7_UP_TIMER12_IRQn,
             RCU_TIMER12,
        },
        {0},
        {
            1000000,
            1000,
            0xffff,
            HWTIMER_CNTMODE_UP, /* count up mode  */
        }
    },
#endif
#ifdef BSP_USING_TIM13
    {
        "timer13",
        {
             TIMER13,
             TIMER7_TRG_CMT_TIMER13_IRQn,
             RCU_TIMER13,
        },
        {0},
        {
            1000000,
            1000,
            0xffff,
            HWTIMER_CNTMODE_UP, /* count up mode  */
        }
    },
#endif
};

static void GD32_TIM_IRQHandler(gd32_hwtimer_device *timer_dev)
{
    rt_device_hwtimer_isr(&timer_dev->hwtimer_dev);
    timer_interrupt_flag_clear(timer_dev->hw_data.timer_periph, TIMER_INT_FLAG_UP);
}

#if defined(BSP_USING_TIM0) || defined(BSP_USING_TIM9)
void TIMER0_UP_TIMER9_IRQHandler(void)
{
    rt_interrupt_enter();

    if(timer_interrupt_flag_get(g_gd32_hwtimer[TIM0_INDEX].hw_data.timer_periph, TIMER_INT_FLAG_UP))
        GD32_TIM_IRQHandler(&g_gd32_hwtimer[TIM0_INDEX]);

    if(timer_interrupt_flag_get(g_gd32_hwtimer[TIM9_INDEX].hw_data.timer_periph, TIMER_INT_FLAG_UP))
        GD32_TIM_IRQHandler(&g_gd32_hwtimer[TIM9_INDEX]);

    rt_interrupt_leave();
}
#endif

#ifdef BSP_USING_TIM1
void TIMER1_IRQHandler(void)
{
    rt_interrupt_enter();
    GD32_TIM_IRQHandler(&g_gd32_hwtimer[TIM1_INDEX]);
    rt_interrupt_leave();
}
#endif

#ifdef BSP_USING_TIM2
void TIMER2_IRQHandler(void)
{
    rt_interrupt_enter();
    GD32_TIM_IRQHandler(&g_gd32_hwtimer[TIM2_INDEX]);
    rt_interrupt_leave();
}
#endif

#ifdef BSP_USING_TIM3
void TIMER3_IRQHandler(void)
{
    rt_interrupt_enter();
    GD32_TIM_IRQHandler(&g_gd32_hwtimer[TIM3_INDEX]);
    rt_interrupt_leave();
}
#endif

#ifdef BSP_USING_TIM4
void TIMER4_IRQHandler(void)
{
    rt_interrupt_enter();
    GD32_TIM_IRQHandler(&g_gd32_hwtimer[TIM4_INDEX]);
    rt_interrupt_leave();
}
#endif

#ifdef BSP_USING_TIM5
void TIMER5_DAC_IRQHandler(void)
{
    rt_interrupt_enter();

    if(timer_interrupt_flag_get(g_gd32_hwtimer[TIM5_INDEX].hw_data.timer_periph, TIMER_INT_FLAG_UP))
        GD32_TIM_IRQHandler(&g_gd32_hwtimer[TIM5_INDEX]);
    
    rt_interrupt_leave();
}
#endif

#ifdef BSP_USING_TIM6
void TIMER6_IRQHandler(void)
{
    rt_interrupt_enter();
    GD32_TIM_IRQHandler(&g_gd32_hwtimer[TIM6_INDEX]);
    rt_interrupt_leave();
}
#endif

#if defined(BSP_USING_TIM7) || defined(BSP_USING_TIM12)
void TIMER7_UP_TIMER12_IRQHandler(void)
{
    rt_interrupt_enter();

    if(timer_interrupt_flag_get(g_gd32_hwtimer[TIM7_INDEX].hw_data.timer_periph, TIMER_INT_FLAG_UP))
        GD32_TIM_IRQHandler(&g_gd32_hwtimer[TIM7_INDEX]);

    if(timer_interrupt_flag_get(g_gd32_hwtimer[TIM12_INDEX].hw_data.timer_periph, TIMER_INT_FLAG_UP))
        GD32_TIM_IRQHandler(&g_gd32_hwtimer[TIM12_INDEX]);
    
    rt_interrupt_leave();
}
#endif

#ifdef BSP_USING_TIM8
void TIMER0_BRK_TIMER8_IRQHandler(void)
{
    rt_interrupt_enter();

    if(timer_interrupt_flag_get(g_gd32_hwtimer[TIM8_INDEX].hw_data.timer_periph, TIMER_INT_FLAG_UP))
        GD32_TIM_IRQHandler(&g_gd32_hwtimer[TIM8_INDEX]);

    rt_interrupt_leave();
}
#endif

#ifdef BSP_USING_TIM10
void TIMER0_TRG_CMT_TIMER10_IRQHandler(void)
{
    rt_interrupt_enter();

    if(timer_interrupt_flag_get(g_gd32_hwtimer[TIM10_INDEX].hw_data.timer_periph, TIMER_INT_FLAG_UP))
        GD32_TIM_IRQHandler(&g_gd32_hwtimer[TIM10_INDEX]);
    
    rt_interrupt_leave();
}
#endif

#ifdef BSP_USING_TIM11
void TIMER7_BRK_TIMER11_IRQHandler(void)
{
    rt_interrupt_enter();

    if(timer_interrupt_flag_get(g_gd32_hwtimer[TIM11_INDEX].hw_data.timer_periph, TIMER_INT_FLAG_UP))
        GD32_TIM_IRQHandler(&g_gd32_hwtimer[TIM11_INDEX]);
    
    rt_interrupt_leave();
}
#endif

#ifdef BSP_USING_TIM13
void TIMER7_TRG_CMT_TIMER13_IRQHandler(void)
{
    rt_interrupt_enter();

    if(timer_interrupt_flag_get(g_gd32_hwtimer[TIM13_INDEX].hw_data.timer_periph, TIMER_INT_FLAG_UP))
        GD32_TIM_IRQHandler(&g_gd32_hwtimer[TIM13_INDEX]);
    
    rt_interrupt_leave();
}
#endif

static int rt_hwtimer_init(void)
{
    int ret = 0, i = 0;

    rcu_timer_clock_prescaler_config(RCU_TIMER_PSC_MUL4);

    for (; i < sizeof(g_gd32_hwtimer) / sizeof(g_gd32_hwtimer[0]); i++)
    {
        g_gd32_hwtimer[i].hwtimer_dev.ops = &g_gd32_hwtimer_ops;
        g_gd32_hwtimer[i].hwtimer_dev.info = &g_gd32_hwtimer[i].hwtimer_info;

        rcu_periph_clock_enable(g_gd32_hwtimer[i].hw_data.rcu);
        NVIC_SetPriority(g_gd32_hwtimer[i].hw_data.irqn, 0);
        NVIC_EnableIRQ(g_gd32_hwtimer[i].hw_data.irqn);
        timer_interrupt_enable(g_gd32_hwtimer[i].hw_data.timer_periph, TIMER_INT_UP);
        ret = rt_device_hwtimer_register(&g_gd32_hwtimer[i].hwtimer_dev, \
            g_gd32_hwtimer[i].dev_name, &g_gd32_hwtimer[i]);
        if (RT_EOK != ret)
        {
            rt_kprintf("failed register %s, err=%d\n", g_gd32_hwtimer[i].dev_name, \
                ret);
            break;
        }
    }

    return ret;
}
INIT_BOARD_EXPORT(rt_hwtimer_init);
#endif
